import {
    CallHandler,
    ExecutionContext,
    Injectable,
    NestInterceptor,
    NotFoundException,
} from '@nestjs/common';
import { Observable, of } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
import { FastifyReply } from 'fastify';

import { METADATA } from '@libs/common/reflects';

import { ControllerMethod, RenderOption } from '../../types';
import { ServerSideConfigService } from '../../services';
import { createHtml, getErrorRenderMapping, ssrErrorRender } from '../../render';
import { EnvironmentParameters, getEnvironmentValue } from '@libs/common/environment';

@Injectable()
export class RenderReactInterceptor implements NestInterceptor {
    constructor(private configCenter: ServerSideConfigService) {}

    intercept(
        context: ExecutionContext,
        next: CallHandler<any>,
    ): Observable<any> | Promise<Observable<any>> {
        const element = (context.getHandler() as ControllerMethod).reactRootElement;
        if (!element) {
            return next.handle();
        }

        const request = context.switchToHttp().getRequest();
        const reply = context.switchToHttp().getResponse() as FastifyReply;
        const pageName = Reflect.getMetadata(METADATA.PAGE_NAME_METADATA, context.getHandler());

        const options: RenderOption = {
            getConfig: (configName: string) => this.configCenter.get(configName),
            rootElement: element,
            pageName,
            request,
            reply,
        };

        // set to be html
        reply.header('Content-Type', 'text/html;charset=UTF-8');

        const isProd = getEnvironmentValue(EnvironmentParameters.RUN_ENV);

        return createHtml(next.handle(), options).pipe(
            catchError((error) => {
                let statusCode = 500;
                if (error instanceof NotFoundException) {
                    statusCode = 404;
                }

                const errorRender = getErrorRenderMapping(statusCode);
                return of(errorRender({ request, reply, exception: error, isDev: !isProd }));
            }),
        );
    }
}
